package cc.android.testapp.xp.hooker.textmodify.et;

import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.text.SpannableString;
import android.text.style.BackgroundColorSpan;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import cc.android.testapp.util.CLog;
import cc.android.testapp.xp.hooker.textmodify.HookEditText;
import cc.commons.util.reflect.MethodUtil;
import dalvik.system.DelegateLastClassLoader;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;

public class ETFactory extends XC_MethodHook {
    public static ETFactory INSTANCE = new ETFactory();
    private static ClassLoader mLoader = null;
    public static HashMap<Class<?>, IET> mWTView = new HashMap<>();
    public static HashMap<Class<?>, IET> mCachedIs = new HashMap<>();
    public static HashSet<Class<?>> mCachedNot = new HashSet<>();
    public static ArrayList<XC_MethodHook.Unhook> mHooked = new ArrayList<>();

    public static void init(Context pContext) {
        mLoader = pContext.getClassLoader();
        mWTView.clear();
        mCachedIs.clear();
        mCachedNot.clear();
        mHooked.forEach(XC_MethodHook.Unhook::unhook);

        mWTView.put(TextView.class, new ET_TextEdit());
        // WeiXin
        ET_CustomTextView tWView = new ET_CustomTextView();
        if (tWView.init(pContext, new ET_CustomTextView.CTView("com.tencent.neattextview.textview.view.NeatTextView",
                ".a()", ".b(java.lang.CharSequence)"))) {
            mWTView.put(tWView.mClazz, tWView);
        }
        // 自定义
        for (ET_CustomTextView.CTView s : HookEditText.mCTViews) {
            ET_CustomTextView tt = new ET_CustomTextView();
            if (tt.init(pContext, s)) {
                mWTView.put(tWView.mClazz, tt);
            }
        }
        // 在此处设置自动拦截setText方法调用
        // 拦截锁定的TextView
        mHooked.add(XposedHelpers.findAndHookMethod(TextView.class, "setText"
                , CharSequence.class, TextView.BufferType.class, INSTANCE));
        // 拦截自定义Text视图
        for (IET sEt : mWTView.values()) {
            if (!(sEt instanceof ET_CustomTextView)) continue;
            ET_CustomTextView tCTView = (ET_CustomTextView) sEt;
            mHooked.add(XposedBridge.hookMethod(tCTView.mMethod_setText, INSTANCE));
        }

        CLog.log("注册了 " + mWTView.size() + " 个TextView包装器");
    }

    public void beforeHookedMethod(XC_MethodHook.MethodHookParam pParam) {
        View tView = (View) pParam.thisObject;
        if (!HookEditText.mEnableTextedit || !HookEditText.canEdit(tView)) return;
        // 锁定文字
        Object tLockData;
        if ((tLockData = HookEditText.getLockData(tView)) != null)
            pParam.args[0] = tLockData.toString();
        // 高亮
        if (pParam.args[0] != null && HookEditText.canHighLight(tView)) {
            String tText = pParam.args[0].toString();
            SpannableString tSpaStr = new SpannableString(tText);
            tSpaStr.setSpan(new BackgroundColorSpan(Color.RED), 0, tText.length(), 18);
            pParam.args[0] = tSpaStr;
        }
    }

    public static IET getWarppedTextView(View pView) {
        Class<?> tViewC = pView.getClass();
        if (mCachedNot.contains(tViewC)) return null;
        IET tIET = mCachedIs.get(tViewC);
        if (tIET == null) {
            for (Map.Entry<Class<?>, IET> sEntry : mWTView.entrySet()) {
                if (isInstance(sEntry.getKey(), pView)) {
                    tIET = sEntry.getValue();
                    mCachedIs.put(tViewC, sEntry.getValue());
                }
            }
        }
        if (tIET == null) {
            if (!(pView instanceof ViewGroup)) {
                try {
                    Class<?> tClass = pView.getClass();
                    Method tGetText = MethodUtil.getMethod(tClass, "getText", false);
                    Method tSetText = MethodUtil.getMethod(tClass, "setText", CharSequence.class, false);
                    Class<?> tTVClass = tGetText.getDeclaringClass();
                    if (tTVClass == tSetText.getDeclaringClass()) {
                        ET_CustomTextView tWView = new ET_CustomTextView();
                        ET_CustomTextView.CTView tCTView = new ET_CustomTextView.CTView(tTVClass.getName(),
                                ".getText()", ".setText(java.lang.CharSequence)");
                        if (tWView.init(pView.getContext(), tCTView)) {
                            mWTView.put(tTVClass, tWView);
                            mHooked.add(XposedBridge.hookMethod(tWView.mMethod_setText, INSTANCE));
                            CLog.log("自动注册了自定义TextView(" + tTVClass + ")");
                            tIET = tWView;
                        }
                    }
                } catch (IllegalStateException ignored) {
                }
            }
        }
        if (tIET != null) {
            tIET.setOrigin(pView);
            mCachedIs.put(tViewC, tIET);
        } else {
            mCachedNot.add(tViewC);
        }
        return tIET;
    }

    /**
     * 是否为继承的类实例
     * <p>由于目标类是在Xposed环境中加载的,所以可能存在类加载器不同的情况,因此不能直接使用isInstance方法来检查<p/>
     *
     * @param pClazz1 目标类
     * @param pView   要检测的视图
     * @return 是否为子类或自身的实例
     */
    private static boolean isInstance(Class<?> pClazz1, View pView) {
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 && pView.getClass().getClassLoader() instanceof DelegateLastClassLoader) {
                Class<?> t = pView.getClass();
                String tCStr = pClazz1.getName();
                do {
                    if (tCStr.equals(t.getName())) return true;
                    t = t.getSuperclass();
                } while (t != Object.class && t != null);
                return false;
            } else {
                return pClazz1.isInstance(pView);
            }
        } catch (Throwable e) {
            CLog.log("[EditText] isInstance报错", e);
            return false;
        }
    }

    ;
}
